package nature.nano.classinfo;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;

import nature.nano.component.Strings;
import nature.nano.iface.enumer.EnumStringCallback;

class ClassScanner {

	private static void scanClassPaths(EnumStringCallback callback) {
		String delim = Strings.colon;
		if (System.getProperty("os.name").indexOf("Windows") != -1) {
			delim = Strings.semicolon;
		}
		
		String[] pathes = System.getProperty("java.class.path").split(delim);
		for (String path : pathes) {
			callback.onRead(path);
		}		
	}
	
	private static void scanJar(EnumStringCallback callback, String folderName) throws IOException {
		FileInputStream fileStream = new FileInputStream(folderName);
		JarInputStream jarStream = new JarInputStream(fileStream, false);
		
		JarEntry entry = null;
		while ((entry = jarStream.getNextJarEntry()) != null) {
			String entryName = entry.getName();
			
			String className = entryName.replace('/', '.').substring(0, entryName.length() - 6);
			try {
				Class<?> cls = Class.forName(className);
				if ((cls.getModifiers() & Modifier.PUBLIC) != 0) {
					callback.onRead(className);					
				}
			} catch (ClassNotFoundException e) {
				// NOTE: do nothing
			}
			
			jarStream.closeEntry();
		}
		
		jarStream.close();
		fileStream.close();
	}
	
	private static void scanPath(EnumStringCallback callback, String folderName, String packageName) throws IOException {
		File folder = new File(folderName);
		if (folder == null || !folder.exists())
			return;
		
		if (folder.isFile()) {
			scanJar(callback, folderName);
			return;
		}
		
		for (File item : folder.listFiles()) {
			String itemPath = item.getAbsolutePath();
			String itemName = packageName + "." + item.getName();
			if (packageName == null || packageName.length() == 0) {
				itemName = item.getName();
			}
			
			if (item.isDirectory()) {
				scanPath(callback, itemPath, itemName);
			} else if (item.isFile()) {
				String lower = itemPath.toLowerCase();
				if (lower.endsWith(".class") && lower.indexOf("$") <= 0) {
					callback.onRead(itemName.substring(0, itemName.length() - 6));
				} else if (lower.endsWith(".jar")) {
					scanJar(callback, itemPath);
				}
			}
		}
	}
	
	private static void scanOrigin(final EnumStringCallback callback) {
		scanClassPaths(
			new EnumStringCallback() {
				public void onRead(String path) {
					try {
						scanPath(callback, path, "");
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			});
	}

	public static void scan(final EnumStringCallback callback) {
		final List<String> clazzs = new ArrayList<String>();

		scanOrigin(
			new EnumStringCallback() {
				public void onRead(String clazz) {
					clazzs.add(clazz);
				}
			});
		
		boolean[] flags = new boolean[clazzs.size()];
		for (int i = 0; i < flags.length; ++i) {
			flags[i] = true;
		}
		
		for (int i = 0; i < flags.length; ++i) {
			String a = clazzs.get(i);
			for (int j = i + 1; j < flags.length; ++j) {
				if (!flags[i] || !flags[j]) {
					continue;
				}
				String b = clazzs.get(j);
				if (a.endsWith(b)) {
					flags[i] = false;
				} else if (b.endsWith(a)) {
					flags[j] = false;
				}				
			}
		}
		
		for (int i = 0; i < flags.length; ++i) {
			if (flags[i]) {
				callback.onRead(clazzs.get(i));
			}
		}
	}
	
	public static void main(String[] args) {
		scan(
			new EnumStringCallback() {
				public void onRead(String clazz) {
					System.out.println(clazz);
				}
			});		
	}
	
}